home *** CD-ROM | disk | FTP | other *** search
/ Fritz: All Fritz / All Fritz.zip / All Fritz / FILES / PROGSCAL / TINYPASC.LZH / TU.DOC < prev    next >
Text File  |  1986-06-03  |  100KB  |  2,047 lines

  1.  
  2.  
  3.  
  4.                            Pascal in 25 Rules or Less
  5.  
  6.                                William A. Barrett
  7.              QCAD Systems, Inc., 1164 Hyde Ave, San Jose, CA 95129
  8.  
  9.             In  this  article,  we're  going to design and implement a
  10.           small  subset  Pascal  compiler,  using  the  Turbo   Pascal
  11.           compiler  and  a couple of other software tools that you can
  12.           purchase for a nominal charge.  It is capable of translating
  13.           a  Pascal  subset  program  into  8086   symbolic   assembly
  14.           language, which can then be assembled into a running program
  15.           on an IBM PC.
  16.             When  you  look  at  our tiny Pascal, it may strike you as
  17.           being  so  ridiculously  simple  that  it  has   no   useful
  18.           applications.   However,  as  we'll explain, it provides all
  19.           the important features of a high-level programming language,
  20.           and can be extended indefinitely  by  writing  more  support
  21.           functions.
  22.             By  reading  this  article,  or  better,  by  ordering the
  23.           support software described at the end of this  article,  you
  24.           will  not  only have your own extensible compiler going, but
  25.           will  have learned  how  language translators and  compilers
  26.           are organized  and written.   So... carry on, please!
  27.  
  28.  
  29.           Tiny Pascal Features
  30.  
  31.             A full Pascal language, like Turbo, has  about  a  hundred
  32.           features, and  a  large  support  library.    We're going to
  33.           choose just a few features, and  simplify  the  language  to
  34.           such an extent that it probably isn't fair to call it Pascal
  35.           -- it just resembles Pascal in some ways.  In particular, we
  36.           want  a  description  of  our  language  in just twenty-five
  37.           production rules  or  less,  since  the  Qparser  demo  pack
  38.           supports just  that  many.    Twenty-six rules, and the demo
  39.           system won't work!  Does this sound like  a  contest  you've
  40.           entered?
  41.             Anyway,   we've  managed  to  pack  all  this  into  those
  42.           twenty-five rules:
  43.  
  44.               * All four arithmetic operations in  full  expressions
  45.             -- but on 16-bit integers only.
  46.               *  Assignment,  IF-THEN-ELSE,  BEGIN-END  blocks,  and
  47.             WHILE-DO.
  48.               * Functions with parameters
  49.               * READ, WRITE, WRITELN with limited string support
  50.               * Global variables
  51.               * Integer literals
  52.  
  53.             Although our feature list is very short, it's large enough
  54.           to write some impressive and useful programs.  The reason is
  55.           that tiny Pascal supports functions that can return values.
  56.           If  you  need  a  capability,  then  you  can always write a
  57.           function to provide that capability.
  58.  
  59.           A Tiny Pascal Program
  60.  
  61.              A complete program written in tiny  Pascal  follows  this
  62.           paragraph.  You should be able to follow what it does.  When
  63.           you  run this through the tiny Pascal compiler, you will get
  64.  
  65.  
  66.                                      page 1
  67.  
  68.  
  69.                            Pascal in 25 Rules or Less
  70.  
  71.  
  72.           an assembler program that  is  ready  to  be  assembled  and
  73.           executed on your PC.  That program will also contain all the
  74.           high-level tiny  Pascal  statements  as comments.  The fully
  75.           compiled and assembled listing is given near the end of this
  76.           article.
  77.  
  78.             {TURUN -- A sample program written in Tiny Pascal }
  79.             var I, J, K, PROBLEM;
  80.  
  81.             {*********************}
  82.             function ISLESS(N1, N2);
  83.             begin  {returns 1 if n1<n2, 0 otherwise}
  84.               if n2-n1 then isless:=1   {truth value test is >0}
  85.               else isless:=0;
  86.               end;
  87.  
  88.             function ADDEMUP(LOWER, UPPER, SUM);
  89.             begin end;    {makes it a forward declaration}
  90.  
  91.             {*********************}
  92.             function ISEQUAL(N1, N2);
  93.             begin
  94.               if n2-n1 then isequal:=0   {false}
  95.               else
  96.               if n1-n2 then isequal:=0
  97.               else isequal:=1;
  98.               end;
  99.  
  100.             {***********************}
  101.             function ADDEMUP(LOWER, UPPER, SUM);
  102.                   {SUM is a local}
  103.             begin
  104.               sum:=0;
  105.               while isless(lower, upper) do begin
  106.                 sum:=sum+lower;
  107.                 lower:=lower+1;
  108.                 end;
  109.               addemup:=sum+lower;  { the last one was left out }
  110.               end;
  111.  
  112.             {*********************}
  113.             function MAIN(SUM, UPPER);
  114.             begin
  115.               i:=1;
  116.               j:=i+5;
  117.               k:=j-16;
  118.               problem:=i+(j*k);
  119.               writeln('I: ', i, ' J: ', j, ' K: ', k, ' Problem: ', problem);
  120.               write('Enter upper ');
  121.               upper:=read;
  122.               sum:=addemup(1, upper);  {sum of integers 1..upper}
  123.               if isequal(sum, (upper*(upper+1))/2) then
  124.                 writeln('Sum = ', sum)
  125.               else begin
  126.                 writeln('BUG: Sum = ', sum, '; should be ',
  127.                            (upper*(upper+1))/2);
  128.                 end;
  129.               end;
  130.  
  131.  
  132.                                      page 2
  133.  
  134.  
  135.                            Pascal in 25 Rules or Less
  136.  
  137.  
  138.  
  139.  
  140.           More About Tiny Pascal
  141.  
  142.             Our functions pass parameters by VALUE only -- there's  no
  143.           VAR parameter capability.  However, a function can return an
  144.           integer value, or it can set the value of a global variable.
  145.           That's  a  bit awkward sometimes, but it's still better than
  146.           assembler.
  147.             There are no Boolean data types as such, but the  language
  148.           uses 0   and   1  as  a  substitute  where  necessary.    In
  149.           particular, the expression in an IF  or  a  WHILE  tests  an
  150.           integer    value    for    greater-than-zero    (true)    or
  151.           less-than-or-equal-to-zero (false).   That's  good  enough.
  152.           For example, you would write
  153.  
  154.              if n1-n2 then ...
  155.  
  156.           in tiny Pascal, which is equivalent to
  157.  
  158.              if n1-n2>0 then ...  or
  159.              if n1>n2 then ...
  160.  
  161.           in full  Pascal.   The comparison operators can of course be
  162.           added with more production rules.
  163.             There are also no local variables in tiny Pascal,  but  it
  164.           turns out they really aren't needed.  For example,
  165.  
  166.             function ADDEMUP(LOWER, UPPER: integer): integer;
  167.               var SUM: integer;
  168.             begin ...  end;
  169.  
  170.           in full Pascal can be written
  171.  
  172.             function ADDEMUP(LOWER, UPPER, SUM);
  173.             begin ...  end;
  174.  
  175.           in tiny  Pascal.    Note  that we don't bother with the type
  176.           designators (i.e.    :  integer)  since  only  one  type  is
  177.           supported.  Also our compiler is arranged so that if ADDEMUP
  178.           is called with only two parameter values, SUM is initialized
  179.           to 0.  For example,
  180.  
  181.             addemup(5, 10)
  182.  
  183.           causes LOWER=5,  UPPER=10, and SUM=0 inside ADDEMUP.  So you
  184.           see the extra formal parameters have the same effect as  the
  185.           local variables  in  full  Pascal.   Of course, you can also
  186.           call ADDEMUP with all three parameters, or none -- that's an
  187.           unsafe defect in tiny Pascal which can't be avoided with our
  188.           tight production rule limit.  You'll just have to be sure to
  189.           call functions with the correct number of parameters.
  190.             Functions can be called within expressions, or on  a  line
  191.           by themselves.    For  example, both of these statements are
  192.           OK:
  193.  
  194.             addemup(5, 10); {returned value is ignored}
  195.             x := y - addemup(5, 10);  {returned  value  is  subtracted
  196.  
  197.  
  198.                                      page 3
  199.  
  200.  
  201.                            Pascal in 25 Rules or Less
  202.  
  203.  
  204.           from y}
  205.  
  206.             Literal  strings  are  permitted  only  within  a WRITE or
  207.           WRITELN statement; they're intended to decorate messages  to
  208.           the standard  output  device  only.   If you want strings to
  209.           appear in expressions and other  functions,  then  you  will
  210.           have to introduce declarations for them and type designators
  211.           -- that'll take more productions.
  212.  
  213.           READ and WRITE
  214.  
  215.             Tiny  Pascal supports three I/O functions: READ, WRITE and
  216.           WRITELN.  READ is called with no parameters, and returns  an
  217.           integer value entered at the keyboard.
  218.             WRITE  takes any number of integer expressions or strings,
  219.           and writes them  to  the  console  screen  in  left-to-right
  220.           order.   No  line  return  is emitted at the end, so you can
  221.           call WRITE several times  before  sending  a  line  return.
  222.           WRITELN  is  the  same as WRITE, except that it emits a line
  223.           return after writing its parameters.  Both these can take no
  224.           parameters if you like -- WRITE does  nothing,  and  WRITELN
  225.           emits a return.
  226.             It  happens  that  our integers will be read or written in
  227.           hexadecimal for simplicity, but you can  easily  change  the
  228.           STDIO.HDR file to support decimal notation instead.
  229.  
  230.           The MAIN Program
  231.  
  232.             There must be one function whose name is MAIN in each tiny
  233.           Pascal program.   That'll  be the first function called.  It
  234.           may have parameters, but we  haven't  provided  any  way  of
  235.           passing   parameters   to  it  from  the  console  --  Turbo
  236.           parameters  are  strings  anyway  and  would  have   to   be
  237.           translated into integers somehow.
  238.             MAIN  can  of  course  call  any other procedure, and it's
  239.           through procedure calls and use of  the  control  structures
  240.           that algorithms can be built up.
  241.  
  242.           Recursion and FORWARD Functions
  243.  
  244.             Tiny  Pascal  supports  recursion  --  a function can call
  245.           itself any number of times.  Each time a function is called,
  246.           its local variables are pushed onto a runtime  stack,  along
  247.           with its  return address.  A special 8086 register -- the BP
  248.           register -- is used to mark the position of these  variables
  249.           so that  they can be accessed from within a function.  We'll
  250.           explain just how that works later.
  251.             Sometimes you'll have a procedure  A  that  calls  B  that
  252.           calls A.   The  problem is that A needs a declaration for B.
  253.           You can supply that in tiny Pascal by writing a function for
  254.           B with an empty Stmt, like this:
  255.  
  256.             function B(P1, P2); begin end;
  257.  
  258.           You can even omit the begin end; if you want to.   The  tiny
  259.           Pascal  compiler  will recognize this as a so-called forward
  260.           declaration.  In full Pascal, you'd write it like this:
  261.  
  262.  
  263.  
  264.                                      page 4
  265.  
  266.  
  267.                            Pascal in 25 Rules or Less
  268.  
  269.  
  270.             function B(P1, P2: integer): integer; forward;
  271.  
  272.           The Grammar
  273.  
  274.             Computer  languages  are  described  by   a   context-free
  275.           grammar, which  is  just a list of production rules.  Here's
  276.           the grammar for tiny Pascal, all twenty-five rules of it:
  277.  
  278.               \ TU.GRM  -- tiny Pascal grammar held to 25 productions
  279.             Goal -> FDeclList
  280.             FDeclList -> FDeclList FuncDecl ;
  281.                       -> FuncDecl ;
  282.             FuncDecl -> FUNCTION <identifier> ( ExprList ) ; Stmt  #FDECL
  283.                      -> VAR ExprList      #VDECL   \ Global variables
  284.                            \ ExprList must be identifiers only
  285.             Stmt -> <identifier> := Expr   #ASSIGN
  286.                  -> IF Expr THEN Stmt ELSE Stmt  #IFTHEN
  287.                  -> WHILE Expr DO Stmt     #WHILEDO
  288.                  -> BEGIN StmtList END     #BLOCK
  289.                  -> Expr                   #SEXPR  \ procedure call only!
  290.             StmtList -> StmtList Stmt ;    #STLIST2
  291.                      -> <empty>
  292.             Expr -> Expr + Term            #ADDOPR
  293.                  -> Expr - Term            #SUBOPR
  294.                  -> Term
  295.             Term -> Term * Primary         #MPYOPR
  296.                  -> Term / Primary         #DIVOPR
  297.                  -> Primary
  298.             Primary -> ( Expr )            #PAREN
  299.                     -> <integer>          \ only type INTEGER supported
  300.                     -> <string>
  301.                     -> <identifier>       \ variable or function call w/o parameters
  302.                     -> <identifier> ( ExprList )      #FUNCP
  303.             ExprList -> ExprList , Expr    #EXPRLIST2
  304.                      -> Expr               #EXPRLIST1
  305.  
  306.             First, a word about the notation.  Each rule takes up  one
  307.           line.   A  backslash starts a comment, which runs to the end
  308.           of the line.  The pound sign (#) marks  a  production  flag,
  309.           which  we'll  use  as  a  kind  of  handle in the compiler's
  310.           parser.
  311.             A rule with nothing to  the  left  of  the  symbol  ->  is
  312.           assumed to  have the same symbol as the preceding rule.  For
  313.           example, the third rule is written
  314.  
  315.                         -> FuncDecl ;
  316.  
  317.           but actually means
  318.  
  319.             FDeclList -> FuncDecl ;
  320.  
  321.             The first rule, with the left member Goal, expands into  a
  322.           complete tiny  Pascal  program.    From the second and third
  323.           rules, a program is a sequence  of  FuncDecl,  separated  by
  324.           semicolons.
  325.             Each  FuncDecl in turn is either a FUNCTION declaration or
  326.           a VAR declaration, according to the fourth and fifth rules.
  327.           In  fact, we will insist that a program consist only of some
  328.  
  329.  
  330.                                      page 5
  331.  
  332.  
  333.                            Pascal in 25 Rules or Less
  334.  
  335.  
  336.           VAR declarations (the  global  variables)  followed  by  the
  337.           FUNCTION declarations.
  338.             Each    FUNCTION   declaration   carries   a   name   (the
  339.           <identifier>),  an  expression  list  (with  at  least   one
  340.           expression), and a statement Stmt.
  341.             Note  that  a  Stmt  can  be a list of statements StmtList
  342.           enclosed in a BEGIN-END pair, so that the body of a function
  343.           can consist of one, two, three or more statements.
  344.             There are four  kinds  of  statement,  an  assignment,  an
  345.           IF-THEN-ELSE,   a   WHILE-DO,  a  BEGIN-END  block,  and  an
  346.           expression -- which in fact must be just a  function  call.
  347.           Note   that   the   ELSE   must  always  be  present  in  an
  348.           IF-THEN-ELSE, unlike full Pascal.
  349.             Also note that Stmt can be <empty>, i.e.  absent.  Thus
  350.  
  351.              if a-b THEN ELSE BEGIN ...  END
  352.  
  353.           is a legal statement.
  354.             For expressions, we support all four arithmetic operations
  355.           on our integers, as well as parenthesizing.  The  production
  356.           rules  guarantee  that  the  usual  Pascal  precedence rules
  357.           apply, and that things inside parentheses will be  evaluated
  358.           first.
  359.             The form  <integer>  stands  for  any  unsigned  integer.
  360.           <string> stands for a Pascal literal string, for example
  361.  
  362.              'Here is a Pascal string'
  363.  
  364.           The form <identifier> stands for  any  legal  Pascal  name.
  365.           Inside  an  expression, the name could stand for a global or
  366.           local variable, or a function name.  As a function name, the
  367.           function will  be  called  with  zero  or  more  parameters,
  368.           returning some value.
  369.  
  370.           Compiler Overview
  371.  
  372.             Let's  first take a broad look at our compiler, then we'll
  373.           look at some of the details.  We need to look at the  forest
  374.           before we  study  the  trees.    (The  details  are  in fact
  375.           voluminous as you might expect, and are best  examined  from
  376.           the source files directly using your favorite editor.)
  377.             We'll   be   using  a  compiler  generator  tool  for  the
  378.           backbreaking part  of  the  job  --  our  choice  is  called
  379.           Qparser, and is available from QCAD Systems, Inc.
  380.             Qparser  accepts  the  25-rule grammar we've written above
  381.           and  generates   a   complete   Pascal   program   from   it
  382.           automatically.   All  you  have  to  do  is run two programs
  383.           called LR1 and LR1P with the  appropriate  file  names,  and
  384.           you'll get   a   Turbo   Pascal   source  file.    (Specific
  385.           instructions are included with the product.)
  386.             Now Qparser only generates a parser --  that's  a  program
  387.           that  will  take a tiny Pascal source file and break it down
  388.           into small steps.  Each  step  will  be  a  production  rule
  389.           reduce or  apply operation.  We need to add some more Pascal
  390.           source code to that  `front-end'  compiler,  to  get  it  to
  391.           generate assembler code.
  392.             In particular, the compiler will:
  393.  
  394.  
  395.  
  396.                                      page 6
  397.  
  398.  
  399.                            Pascal in 25 Rules or Less
  400.  
  401.  
  402.               1.   Construct  an  abstract  syntax  tree (AST) for a
  403.             whole function.  This will be built  from  unit  records
  404.             allocated  from  the heap, linked together with pointers
  405.             in various  ways.    The  AST  will  carry  a   complete
  406.             description    of   all   the   variables,   statements,
  407.             expressions, function calls, etc.  in the function.
  408.               2.   When  a  function  AST  is  complete,  its  local
  409.             variables  will  be  added to a symbol table, along with
  410.             their positions relative to register BP.
  411.               3.  Then the statements  and  expressions  are  turned
  412.             into 8086 assembly code through a recursive tree-walking
  413.             procedure called   EVAL.    EVAL  is  rather  simple  to
  414.             describe, and -- as we  shall  see  --  produces  rather
  415.             tight 8086 code for our integer operations, assignments,
  416.             function calls, IF-THENs, and WHILE-DOs.  It's also easy
  417.             to understand, extend or modify.
  418.  
  419.             The  tiny  Pascal  compiler  acts  somewhat  like  a batch
  420.           compiler -- it collects all the lines for a  function,  then
  421.           emits all  the  code  for the function.  We've tried to make
  422.           the resulting assembly code easy  to  read  by  copying  the
  423.           function  source lines as comments, and by writing the names
  424.           of local variables as comments.  (Local variable  references
  425.           will  all appear as offsets from register BP in the assembly
  426.           code, rather than as names.)
  427.  
  428.           Designing the Compiler
  429.  
  430.             Once we've written the grammar, we  can  pass  it  through
  431.           Qparser,  then try out the automatically-generated parser on
  432.           a sample program -- such as TURUN given earlier.  That  will
  433.           verify that our grammar describes what we expect it to.
  434.             The next big step is designing compiler data structures to
  435.           support  the  AST  that  will  hold  all  the  features of a
  436.           complete function.  It turns out that's easy to do,  too  --
  437.           the  Qparser files contains a sample Pascal record structure
  438.           designed for that purpose, called a SEMREC.  Here's what the
  439.           generic  SEMREC  looks  like  as  supplied  in  the  Qparser
  440.           software:
  441.  
  442.             type
  443.               SEMTYPE = (OTHER, IDENT, FIXED, STRNG);
  444.               SEMRECP = ^semrec;
  445.               SEMREC = record
  446.                          case SEMT: semtype of   { semantic stack structure }
  447.                            ident: (SYMP: symtabp);
  448.                            fixed: (NUMVAL: integer);
  449.                            strng: (STX: integer);       {index to string table}
  450.                            end;
  451.  
  452.             This supports only three kinds of object: an identifier, a
  453.           fixed-point integer  and a string.  Identifiers are actually
  454.           written into a symbol table -- the symtabp is a pointer to a
  455.           symbol table record.  An integer has a  value,  NUMVAL.    A
  456.           string  is  written  to a global packed array of char called
  457.           STRTAB, starting at the index STX, and ending on a chr(0).
  458.             Note that the type SEMRECP is  a  pointer  to  a  SEMREC.
  459.           We're  going  to build our AST around a tree whose nodes are
  460.  
  461.  
  462.                                      page 7
  463.  
  464.  
  465.                            Pascal in 25 Rules or Less
  466.  
  467.  
  468.           linked by SEMRECP pointers.  Each of the  SEMREC  structures
  469.           will  be allocated from the Pascal heap as needed, and we'll
  470.           dispose of them all after we're through with  them,  at  the
  471.           end of scanning a tiny Pascal function.
  472.  
  473.           Arithmetic Operators
  474.  
  475.             The  AST  for the four arithmetic operators is the easiest
  476.           to grasp.  We extend SEMREC to these operators by adding two
  477.           lines to our SEMREC declaration.  The result is:
  478.  
  479.              SEMREC = record
  480.                          case  SEMT:  semtype  of  {  semantic   stack
  481.           structure }
  482.                            ident: (SYMP: symtabp);
  483.                            fixed: (NUMVAL: integer);
  484.                            strng:  (STX:  integer);  {index  to string
  485.           table}
  486.                            addop, subop, mpyop, divop:
  487.                                   (LEFT, RIGHT: semrecp);
  488.                            end;
  489.  
  490.           Of course, we also need to add  the  enumerated  type  names
  491.           addop, etc.  to the SEMTYPE list:
  492.  
  493.               SEMTYPE = (OTHER, IDENT, FIXED, STRNG,
  494.                                 ADDOP, SUBOP, MPYOP, DIVOP);
  495.  
  496.             Let's  look  at an example of an arithmetic expression and
  497.           see how it is expressed as an AST: X * (Y-Z) / 15.
  498.             Figure 1 shows the AST we want.  The -  operator  must  be
  499.           performed  first,  since  we  need  its  result before the *
  500.           operator, and it in turn is needed before the division / can
  501.           be performed.  This  ordering  is  clearly  dictated  by  an
  502.           inflexible  AST  rule:  the  children  of  a  node  must  be
  503.           evaluated before the node itself.  The leaves  of  our  tree
  504.           are identifiers (X, Y, Z) or numbers (15).  A number clearly
  505.           stands  for  itself,  while  each identifier stands for some
  506.           numeric value at runtime.  In fact, an  identifier  will  be
  507.           associated  with  some memory address whose contents will be
  508.           fetched in an appropriate instruction.
  509.             Let's next see how the AST of figure 1 would be  described
  510.           by SEMREC  structures  linked  by pointers.  That's shown in
  511.           figure 2, where each SEMREC is a box, and its LEFT and RIGHT
  512.           pointers are connected to other SEMREC boxes.  The root is a
  513.           SEMREC whose SEMT=divop.  Its LEFT child is  another  SEMREC
  514.           whose SEMT=mpyop.    The  leaves  are also SEMREC's, since a
  515.           SEMRECP pointer must point to a SEMREC, but the  leaf  nodes
  516.           are either  SEMT=ident or SEMT=fixed.  The ident nodes point
  517.           to a symbol table entry  which  will  carry  the  identifier
  518.           name, its type, its address and possible other information.
  519.  
  520.           How the AST is Built
  521.  
  522.             The AST  will  be  built through parser actions.  We don't
  523.           have the space here to go into the  theory  of  the  parsing
  524.           machine  -- that's described in several different references
  525.           [1, 2].  However, we'll try to explain enough to see how our
  526.  
  527.  
  528.                                      page 8
  529.  
  530.  
  531.                            Pascal in 25 Rules or Less
  532.  
  533.  
  534.           AST gets built.
  535.             Let's look at our arithmetic expression again: X * (Y-Z) /
  536.           15.  When the parser scans this,  it  will  go  through  the
  537.           following sequence of production steps:
  538.  
  539.             Primary -> <identifier>          ; <identifier> = X
  540.             Primary -> <identifier>          ; <identifier> = Y
  541.             Primary -> <identifier>          ; <identifier> = Z
  542.             Expr -> Expr - Term   #SUBOPR    ; Y - Z
  543.             Primary -> ( Expr )   #PAREN     ; (Y-Z)
  544.             Term -> Term * Primary  #MPYOPR  ; X * (Y-Z)
  545.             Primary -> <integer>             ; 15
  546.             Term -> Term / Primary  #DIVOPR  ; X * (Y-Z) / 15
  547.             Expr -> Term
  548.  
  549.           We've left out several intermediate steps, but these are the
  550.           important ones.  Essentially, a derivation of our expression
  551.           is being reconstructed in reverse order -- we start with the
  552.           expression,  match  various  production  right  parts to the
  553.           expression, and eventually end up with a  single  expression
  554.           node -- Expr.
  555.             Notice  how the identifiers X, Y and Z are picked up first
  556.           and `reduced' to a Primary.  Then the Y and Z primaries  are
  557.           combined in  the  SUBOPR  production.   That agrees with our
  558.           notion that the subtraction has to come first.
  559.             The parentheses are covered by the  PAREN  production;  we
  560.           can   then   reduce   the  multiplication  with  the  MPYOPR
  561.           production.
  562.             The integer 15 gets scanned next; we didn't need it  until
  563.           now,  but  it's  involved  in  the division, and finally the
  564.           division is covered through the DIVOPR production.
  565.             This sequence of events is the same as if we evaluated the
  566.           expression with  a  reverse-Polish  calculator,  like  those
  567.           manufactured by  the  Hewlett-Packard  Co.  We enter X, then
  568.           enter Y, enter Z, subtract, multiply, enter 15 and  divide.
  569.           On each `enter', a value is pushed into a stack, which saves
  570.           it for future reference.  Each operation is between a number
  571.           on  the  stack  top and a number just below it; the stack is
  572.           popped and the result appears on the stack top.
  573.             Thus when the multiply is performed, it acts  on  the  two
  574.           operands X and (Y-Z).  When the divide is performed, it acts
  575.           on the operands X*(Y-Z) and 15.
  576.  
  577.           Using the Semantics Stack
  578.  
  579.             Qparser  also provides a stack that works almost like that
  580.           on a reverse-Polish calculator.  The difference is that  the
  581.           Qparser  stack  can  be  very  deep,  and  it  is  keyed  to
  582.           production apply actions.  Let us explain what that means --
  583.           it's crucial to understanding how our AST is built.
  584.             Qparser's stack is called SEM.   SEM  is  declared  as  an
  585.           array of  SEMRECP  pointers.    The stack top index is named
  586.           TOS, which increases as more items are pushed onto the stack
  587.           and decreases as the stack is popped.  Thus SEM[TOS]^ points
  588.           to a SEMREC object on the  top  of  the  stack;  SEM[TOS-1]^
  589.           points to  an object just below the stack top, etc.  (SEM[n]
  590.           may also be NIL, which means there is no object.)
  591.             Now  the  Qparser  software  will  perform  the  following
  592.  
  593.  
  594.                                      page 9
  595.  
  596.  
  597.                            Pascal in 25 Rules or Less
  598.  
  599.  
  600.           services for you in response to scanning its input source:
  601.  
  602.             1.   Each  tagged production will cause procedure APPLY to
  603.           be called with an  integer  parameter  that  designates  the
  604.           production.  The APPLY procedure looks like this:
  605.  
  606.             procedure APPLY(PFLAG: integer; var TSEMP: semrecp);
  607.             begin
  608.               case PFLAG of
  609.                 ADDOPR: ...
  610.                 ASSIGN: ...
  611.                 BLOCK: ...
  612.                  etc.
  613.                 WHILEDO: ...
  614.                 VDECL: ...
  615.                 end
  616.               end;
  617.  
  618.           Note  that  PFLAG  causes  an immediate branch to one of the
  619.           legs of the CASE statement -- a leg that corresponds to  one
  620.           particular production.
  621.             2.   When  a  production such as Term -> Term * Primary is
  622.           invoked through an APPLY  call,  the  top  three  SEM  stack
  623.           elements   will  be  aligned  with  Term,  *,  and  Primary,
  624.           respectively.  SEM[TOS] will point to  something  associated
  625.           with  Primary, SEM[TOS-1] will be NIL (since * needn't carry
  626.           information),  and  SEM[TOS-2]  will  point   to   something
  627.           associated with Term.
  628.             This is an important concept, and is illustrated in figure
  629.           3.   Just  before  our  APPLY  action,  the  stack has three
  630.           elements on its top, which carry pointers to  the  roots  of
  631.           the trees  X and (Y-Z), respectively.  (See figure 2 for the
  632.           whole tree.)
  633.             3.  In the APPLY procedure, you have an opportunity to  do
  634.           something about the SEM information, and to return a pointer
  635.           to a SEMREC through the var parameter TSEMP.
  636.             Consider  production  Term -> Term * Primary again (figure
  637.           3).  We assume that SEM[TOS-2] (= Term)  points  to  a  tree
  638.           node, as  does SEM[TOS] (= Primary).  We want to construct a
  639.           new SEMREC tree node whose SEMT is MPYOP,  whose  LEFT  will
  640.           point to  Term and whose RIGHT will point to Primary.  TSEMP
  641.           will be assigned to point to this new node.  That's easy  --
  642.           here's the Pascal code fragment needed for the job:
  643.  
  644.              MPYOPR: { Term -> Term * Primary }
  645.                begin
  646.                  new(tsemp); {allocate a new node}
  647.                  with tsemp^ do begin {and fill in its record fields}
  648.                    left:=sem[tos-2]; {points to Term}
  649.                    right:=sem[tos]; {points to Primary}
  650.                    semt:=mpyop;
  651.                    end
  652.                  end;
  653.  
  654.             4.   When  APPLY  returns,  the  var  parameter  TSEMP  is
  655.           returned to the parser, which will (a)  strip  off  the  top
  656.           three  pointers  on  the  SEM  stack then (b) push the TSEMP
  657.           pointer.  Notice that the top three  pointers  have  already
  658.  
  659.  
  660.                                     page 10
  661.  
  662.  
  663.                            Pascal in 25 Rules or Less
  664.  
  665.  
  666.           been linked  to  TSEMP,  so  losing them is no big deal.  By
  667.           pushing TSEMP on the stack, we have  effectively  associated
  668.           our  new  tree  root  with  the Term on the left side of the
  669.           production Term -> Term * Primary.
  670.             Refer to figure 4.  This is how the stack will  look  just
  671.           after returning  from  APPLY.    The top three pointers have
  672.           been scraped off, and replaced by the  TSEMP  pointer.    We
  673.           clearly  have a larger tree rooted in the stack top -- again
  674.           look at figure 2 for the whole tree,  and  you'll  see  that
  675.           we've added one node to it.
  676.  
  677.             That closes  the  loop  on our operations.  Eventually our
  678.           new Term will show up again in  the  APPLY  procedure,  this
  679.           time  as an element in the right part of some production; it
  680.           will carry the root of a tree, and that tree will get  built
  681.           into something bigger.
  682.             Certain  actions  are  done  for  us  automatically by the
  683.           parser  and  lexical  analyzer  system  of  Qparser  --  the
  684.           <identifier>,  <integer>  and <string> forms are loaded into
  685.           the SEM stack automatically when and where they are needed.
  686.             For example, consider the production Stmt ->  <identifier>
  687.           := Expr,  tagged  ASSIGN  in  our production set.  When this
  688.           pops up in an APPLY call, there will be a SEMREC pointer  in
  689.           SEM[TOS-2]  that  corresponds  to  the <identifier>; it will
  690.           carry the tag SEMT=ident, and the SYMP pointer will point to
  691.           a symbol table entry that corresponds to the identifier.
  692.             Single productions needn't be tagged --  the  parser  will
  693.           preserve  whatever  is attached to the right member, passing
  694.           it along to the left  member.    (A  single  production  has
  695.           exactly  one  token in its right member, for example Primary
  696.           -> <identifier>).  Also, TSEMP is by default NIL, so  if  we
  697.           choose not to set TSEMP, a NIL will be pushed on the stack.
  698.  
  699.           Other Tree Nodes
  700.  
  701.             Well,  we've seen how an arithmetic expression is built up
  702.           as an AST.  The same principle can be applied to any form in
  703.           our language.  Let's look at  some  others.    Consider  the
  704.           assignment  production  Stmt -> <identifier> := Expr, tagged
  705.           ASSIGN.  We'll use the tag SEMT=assignop for it, and use our
  706.           friends LEFT and RIGHT as pointers to the  <identifier>  and
  707.           Expr.   The  APPLY  code  that  builds  this tree node looks
  708.           almost the same as for the multiply case:
  709.  
  710.              ASSIGNOP: { Stmt -> <identifier> := Expr }
  711.                begin
  712.                  new(tsemp); {allocate a new node}
  713.                  with tsemp^ do begin {and fill in its record fields}
  714.                    left:=sem[tos-2];
  715.                    right:=sem[tos];
  716.                    semt:=assignop;
  717.                    end
  718.                  end;
  719.  
  720.           In fact, it  looks  so  much  alike  that  we've  created  a
  721.           procedure  that  deals with all these binary operator cases,
  722.           called BIN_TREE; this takes one parameter, the  SEMT  value,
  723.           and  expects  to  find a LEFT node in SEM[TOS-2] and a RIGHT
  724.  
  725.  
  726.                                     page 11
  727.  
  728.  
  729.                            Pascal in 25 Rules or Less
  730.  
  731.  
  732.           node in SEM[TOS].
  733.             Not all the tree nodes have binary operators.  Let's  look
  734.           at an expression list, which is carried by two productions:
  735.  
  736.             ExprList -> ExprList , Expr #EXPRLIST2
  737.                      -> Expr #EXPRLIST1
  738.  
  739.           These  don't  look  quite  like our binary operators, but in
  740.           fact can be handled in a very similar way.  We'll create yet
  741.           another SEMT enumerated  type  EXPR_LIST  with  a  LEFT  and
  742.           RIGHT.   LEFT will point to an expression subtree, but RIGHT
  743.           will be NIL or point to another EXPR_LIST.  Figure  5  shows
  744.           the general  idea  for  a  list  of  two  subtrees.    (This
  745.           structure is borrowed from Lisp --  the  LEFT  pointer  acts
  746.           like CAR and the RIGHT pointer acts like CDR).
  747.             The code for these two productions looks like this:
  748.  
  749.             EXPRLIST1:  { ExprList -> Expr }
  750.               if not(is_void(sem[tos])) then begin
  751.                 tsemp:=new_sem(expr_list);
  752.                 tsemp^.left:=sem[tos];
  753.                 end;
  754.             EXPRLIST2:  { ExprList -> ExprList , Expr }
  755.               if not(is_void(sem[tos])) then begin
  756.                 tsemp:=new_sem(expr_list);
  757.                 tsemp^.left:=sem[tos];
  758.                 tsemp:=nconc(sem[tos-2], tsemp);
  759.                 end
  760.               else tsemp:=sem[tos-2];
  761.  
  762.           Some new functions are shown in this code fragment.  NEW_SEM
  763.           allocates  a  SEMREC  from  the  Pascal  heap using NEW, and
  764.           assigns its SEMT to the passed parameter.  More importantly,
  765.           it also initializes LEFT and RIGHT (or  other  pointers)  to
  766.           NIL, rather than letting them be garbage.  NEW_SEM is always
  767.           used  rather  than  NEW  directly  in  order to achieve this
  768.           useful initialization.
  769.             The IS_VOID function tests its SEMREC parameter for  NIL.
  770.           Finally, NCONC takes two parameters, a list L and an element
  771.           E.   It  attaches  the  element  to  the  end of the list by
  772.           altering the RIGHT pointer of the last element of the list L
  773.           to point to the element E.  (Again, this is inspired by  the
  774.           Lisp function of the same name).
  775.             Let's now  look  at  these operations in more detail.  The
  776.           EXPRLIST1 operation must either return NIL or  an  EXPR_LIST
  777.           that points  to the Expr.  It turns out that Expr will never
  778.           be NIL, but we've written it this way for robustness.
  779.             The EXPRLIST2 operation looks at the Expr; if it  is  NIL,
  780.           then  we  can simply return the pointer to the ExprList (it,
  781.           too, may be NIL).  If Expr isn't NIL, then we call NCONC  to
  782.           stitch  it  onto  the  end  of the ExprList, as suggested by
  783.           figure 5.
  784.  
  785.           Function Declaration
  786.  
  787.             It's time to consider the function declaration production
  788.  
  789.              FuncDecl -> FUNCTION <identifier> (  ExprList  )  ;  Stmt
  790.  
  791.  
  792.                                     page 12
  793.  
  794.  
  795.                            Pascal in 25 Rules or Less
  796.  
  797.  
  798.           #FDECL
  799.  
  800.           When  this  pops  up  in  an  APPLY  action, we have scanned
  801.           through the function name (the <identifier>), its  parameter
  802.           list (ExprList),  and  its  body (the Stmt).  Assuming we've
  803.           done all our AST building correctly, the ExprList  and  Stmt
  804.           are the roots of some trees, possibly very large.
  805.             The  scanning  and  parsing action have worked through the
  806.           function  declaration  completely,  but   we   haven't   (1)
  807.           generated  any  code,  nor (2) done anything about declaring
  808.           the procedure or its parameters.
  809.             In fact, we need to perform these actions in this order:
  810.  
  811.               1.  Add the function identifier to the symbol table at
  812.             the global scope level, if it isn't already there.   (It
  813.             could  have  been previously declared with an empty Stmt
  814.             part, i.e.  as a forward.)
  815.               2.  Raise the scope level by one.
  816.               3.  Add the ExprList identifiers to the symbol  table,
  817.             checking each  one  for  multiple  declarations.   These
  818.             should each  be  a  single  identifier  rather  than  an
  819.             expression; we can easily test their tree roots for this
  820.             case and complain about an error.  We did it this way to
  821.             save on  productions.    Each of the identifiers will be
  822.             assigned a  location  in  the  stack  relative  to  base
  823.             register BP -- we'll explain how that works shortly.
  824.               4.  Evaluate  the Stmt tree.  A recursive procedure is
  825.             needed for  this  that  will  work  through  the  entire
  826.             statement tree, emitting code appropriate to each of the
  827.             forms in  our  language.    We'll  see that this is much
  828.             easier than it appears on  the  surface  and  is  almost
  829.             trivial for some of the language forms.  All we need are
  830.             some  simple translation forms in 8086 assembler and our
  831.             AST will unfold very neatly into code.
  832.               During the evaluation  phase,  every  identifier  will
  833.             have  a pointer to a symbol table entry which will carry
  834.             a relative address and a type for the name.   That  will
  835.             be useful in generating instructions for the identifier.
  836.  
  837.           The Symbol Table
  838.  
  839.             We need  to  describe  our symbol table system.  There are
  840.           various ways of organizing symbol  tables;  we're  going  to
  841.           focus on the one used in the Qparser system.
  842.             User  names  are  associated with the <identifier> form in
  843.           the grammar.  The default <identifier> form is a Pascal name
  844.           -- something that starts with a letter  and  continues  with
  845.           letters, digits,  or  underscores.  The first character that
  846.           isn't in this set stops the name -- it could be a  space,  a
  847.           left parenthesis, or whatever.
  848.             Names will be upper cased as in Pascal.
  849.             Note  that  certain  keywords  look  like identifiers, for
  850.           example, IF, WHILE, BEGIN, etc.  The lexical  scanner  scans
  851.           one  of these as though it were an identifier, then searches
  852.           the symbol table for it.  The keywords have previously  been
  853.           entered  and  tagged  as such, therefore a successful `find'
  854.           will immediately indicate that the supposed identifier is in
  855.           fact a keyword.  Making this distinction  is  vital  to  the
  856.  
  857.  
  858.                                     page 13
  859.  
  860.  
  861.                            Pascal in 25 Rules or Less
  862.  
  863.  
  864.           parser,   since   the   keywords   are  important  clues  to
  865.           recognition of the program phrases.
  866.             If the identifier isn't a  keyword,  it  will  be  entered
  867.           anyway under an innocuous tag (user).
  868.             Now  that  we've  discussed  how the symbol table works in
  869.           general, let's look at the symbol table record structure:
  870.  
  871.             type
  872.               SYMBOL = string[maxtoklen];
  873.               SYMTYPE = (RESERVED, SYMERR, USER, VAR_TYPE, FUNC_TYPE);
  874.               SYMTABP = ^symtabtype;
  875.               SYMTABTYPE = record
  876.                              { structure for <identifier>s and keywords }
  877.                              NEXT: symtabp;
  878.                              LEVEL: int;
  879.                              SYM: symbol;
  880.                              case SYMT: symtype of
  881.                                reserved: (TOKVAL: tokrange);
  882.                                var_type: (VADDR: integer);
  883.                                   {relative to BP}
  884.                                func_type:
  885.                                  (FADDR: integer;  {code location}
  886.                                   PBYTES: integer;  {bytes of parameters}
  887.                                   IS_ACTUAL,     {TRUE if an actual declaration
  888.                                                     has been seen}
  889.                                   IS_SYSTEM   {system procedure, demands special
  890.                                                   treatment}
  891.                                     : boolean);
  892.                                end;
  893.  
  894.           There are five classes of  symbol.    RESERVED  is  for  the
  895.           keywords.   SYMERR  is  used  during error recovery when the
  896.           parser  needs  to  make  up  an  identifier  for   insertion
  897.           purposes.    USER   is  a  generic  name  attached  to  user
  898.           identifiers when they are first seen.   VAR_TYPE  refers  to
  899.           the  global  and  local  variable  names  -- its VADDR field
  900.           provides (in the local case only) an offset from BP.
  901.             FUNC_TYPE refers  to  a  declared  function.    These  are
  902.           somewhat complicated in our tiny Pascal.
  903.             FADDR actually isn't used, but could be to refer to a code
  904.           location.  Since we generate symbolic assembly code, we will
  905.           just  produce a symbol procedure label and let the assembler
  906.           worry about where the function will be located.
  907.             PBYTES is the number of bytes  of  formal  parameters  the
  908.           function carries.    We  need  this  in order to generate an
  909.           appropriate EXIT instruction, and also to generate calls.
  910.             IS_ACTUAL will be false until a declaration  with  a  full
  911.           statement body  is seen; after that, IS_ACTUAL will be true.
  912.           We use this to catch such errors as undeclared functions and
  913.           two functions with full statement bodies.
  914.             IS_SYSTEM designates the function as a `system'  function,
  915.           slated for special handling.  Our WRITE, WRITELN, READ and a
  916.           couple of  other  functions  are  in  this class.  These are
  917.           supported by special assembler routines  and  aren't  called
  918.           quite the same way as user-declared functions.
  919.  
  920.             A symbol  name  is stuffed in SYM, which is just a string.
  921.           NEXT carries a link to another  SYMTABTYPE  record,  and  is
  922.  
  923.  
  924.                                     page 14
  925.  
  926.  
  927.                            Pascal in 25 Rules or Less
  928.  
  929.  
  930.           used  in  a  hash  table linked-list system for rapid symbol
  931.           searching.
  932.             The LEVEL parameter designates the scoping  level  of  the
  933.           name.  Reserved names are carried at level -1.  Global names
  934.           are at  level  0 and locals at level 1.  In full Pascal, the
  935.           LEVEL would be incremented by one for every descent  into  a
  936.           lexically nested   procedure   or  function.    Tiny  Pascal
  937.           supports only one global level of procedures, so LEVEL never
  938.           exceeds 1.
  939.  
  940.             There are several major symbol table actions  of  interest
  941.           in tiny   Pascal.     Reserved  names  are  pushed  into  an
  942.           otherwise-empty table as the first action of  the  compiler,
  943.           at level -1.  This action is essentially automatic by virtue
  944.           of the Qparser generator.
  945.             When  an  <identifier> form is seen, the identifier string
  946.           is picked up by the lexical  analyzer  and  entered  in  the
  947.           table, if  not  already  there.   The analyzer also builds a
  948.           SEMREC structure of type IDENT and pushes it  into  the  SEM
  949.           stack at  the appropriate place.  APPLY will therefore `see'
  950.           an IDENT structure of type USER upon the first appearance of
  951.           some user identifier.
  952.             Eventually, we choose to add attributes to the identifier.
  953.           This will occur when a production appears that reveals  what
  954.           attributes are required.  In tiny Pascal, that production is
  955.           the FuncDecl  production  FDECL.   The action required is to
  956.           scan through the ExprList, and change the attributes of  the
  957.           names to   VAR_TYPE,  assigning  VADDR  values  as  we  go.
  958.           Correction: if the name doesn't carry a  USER  tag,  it  may
  959.           have  been  previously  declared  as  a global variable or a
  960.           function; we need  to  override  the  previous  use  without
  961.           destroying the  previous  use.    Our  symbol  table  scheme
  962.           permits  doing  that  by  carrying  successive  names  in  a
  963.           first-in-first-out stack organization.
  964.             Once  these  names  have  been  declared, there will be no
  965.           further declarations of names in  the  function  body;  only
  966.           references   to  names  that  should  previously  have  been
  967.           declared.  These name references will show up as we scan the
  968.           AST in the EVAL function.  Among other things, we watch  out
  969.           for  identifiers  carrying  a USER tag -- these haven't been
  970.           declared anywhere and deserve an error complaint.
  971.  
  972.           Getting Rid of Unwanted Names
  973.  
  974.             When the function body has been fully  evaluated  and  all
  975.           code generated, we need to get rid of the local variables in
  976.           the symbol  table.    A  function  called  CLEARSYM does the
  977.           trick.  It locates all the names in the symbol  table  whose
  978.           LEVEL is  greater  than  0, and removes them from the table.
  979.           That's important, since the  scope  of  a  function's  local
  980.           variables  is  the  lexical  range  of  that function and no
  981.           further.  By doing this, we make it possible to use the same
  982.           name in different procedures without confusing the  compiler
  983.           or assembler.
  984.  
  985.  
  986.  
  987.  
  988.  
  989.  
  990.                                     page 15
  991.  
  992.  
  993.                            Pascal in 25 Rules or Less
  994.  
  995.  
  996.           The Run Time Environment
  997.  
  998.             We  need  to describe how we propose to generate 8086 code
  999.           from our AST.  (If you're not familiar  with  the  8086,  we
  1000.           recommend  [3] as a reference.) There are four kinds of code
  1001.           generation problem confronting us:
  1002.  
  1003.               1) Function declaration -- what  assembler  forms  are
  1004.             needed  to  open a function and what are needed to close
  1005.             it?
  1006.               2) Function call -- how are the parameters passed  and
  1007.             how is the call generated?
  1008.               3) Expression and assignment evaluation -- how are the
  1009.             register and memory instructions used to maximum benefit
  1010.             and efficiency?
  1011.               4)  Control  structures -- how are the conditional and
  1012.             unconditional branches to be coded?
  1013.  
  1014.           We'll address each of these in turn.   Unfortunately,  items
  1015.           (1) and  (2)  depend  on  each  other.    Our procedure call
  1016.           strategy must be defined before we can decide on just how  a
  1017.           call and  a  declaration  are  to be carried out.  That will
  1018.           also  determine  how  our  local  variable   addresses   are
  1019.           computed.  So let's look at function calls first.
  1020.  
  1021.           Function Call Environment
  1022.  
  1023.             Our language is intended to support recursion, which means
  1024.           that  a  given  function  may have been called several times
  1025.           before any exit has taken place.  Each of these  uncompleted
  1026.           calls  is called an activation, and each activation requires
  1027.           an activation record that contains a set of  local  variable
  1028.           values, a return value and a return address.  The activation
  1029.           records  will  be  carved out of the 8086's stack, where the
  1030.           stack top is pointed to by register SP.
  1031.             The micro's stack will also  be  used  to  hold  temporary
  1032.           expression  values,  so  SP  will be moving up and down in a
  1033.           somewhat unpredictable fashion.  We need a  way  of  marking
  1034.           the position of the activation record that doesn't depend on
  1035.           SP.   The  current  activation  record will be pointed to by
  1036.           register BP.
  1037.             Figure 6 shows our activation record design.    The  stack
  1038.           top is  at  the bottom, perversely.  (Does that mean that we
  1039.           programmers don't know which way is up?) In  the  8086,  the
  1040.           PUSH operation causes SP to decrease in numeric value -- the
  1041.           stack  will start at the end of some segment and work toward
  1042.           its beginning.
  1043.             The figure shows an activation record for a function  with
  1044.           three parameters, P1, P2, and P3, in left-to-right order, as
  1045.           it  might  appear  when  we're ready to start executing some
  1046.           function body instructions.  However, the  record  got  that
  1047.           way  partly  through call operations and partly through code
  1048.           at the front end of the function.
  1049.             The function call will 1) reserve  space  for  a  function
  1050.           return  value  through  a  PUSH  AX  operation, 2) PUSH each
  1051.           parameter in their order of appearance, and then 3) CALL the
  1052.           function.  The CALL instruction will push the return address
  1053.           on the stack.  At this point, SP will point to the stack top
  1054.  
  1055.  
  1056.                                     page 16
  1057.  
  1058.  
  1059.                            Pascal in 25 Rules or Less
  1060.  
  1061.  
  1062.           (of course), and  BP  will  be  pointing  at  some  previous
  1063.           activation record deeper in the stack.
  1064.             Among  the first instructions executed within the function
  1065.           will be
  1066.  
  1067.                PUSH BP
  1068.                MOV BP,SP
  1069.  
  1070.           The first of these pushes the previous value of  BP  in  the
  1071.           stack, and the second sets BP to the new stack top.
  1072.             Notice  that  parameter P1 is at BP+8 (each stack location
  1073.           is a 2-byte word), P2 is at BP+6, P3 is  at  BP+4,  and  the
  1074.           function return  value  is  at  BP+10.  These offsets can be
  1075.           calculated by the compiler and associated with each  of  the
  1076.           respective names.  Furthermore, the 8086 instruction set and
  1077.           our  assembler permit us to use forms like 8[BP] to refer to
  1078.           an address offset from BP by +8 bytes.    We  can  therefore
  1079.           easily  generate  symbolic  address references to any of the
  1080.           function's variables.
  1081.  
  1082.           Function EXIT
  1083.  
  1084.             An EXIT from a function is similarly easy.    We  need  to
  1085.           restore  the  previous  BP value to the one carried at BP in
  1086.           the  stack,  then  return,  popping  the  right  number   of
  1087.           parameters from  the stack.  For the configuration of figure
  1088.           6, we therefore need
  1089.  
  1090.                POP BP
  1091.                RET 8
  1092.  
  1093.           We overlooked one thing --  since  we  are  dealing  with  a
  1094.           function,  it's  important  to send the return value back to
  1095.           whatever expression the function was  called  from.    We're
  1096.           going  to  adopt  the  convention that every expression will
  1097.           yield its value in the 16-bit AX  register.    However,  our
  1098.           function's return  value is in the stack.  We therefore need
  1099.           the instruction
  1100.  
  1101.                MOV AX,10[BP]
  1102.  
  1103.           before we POP BP.  Then we can  remove  the  parameters  and
  1104.           return value  from  the  stack upon a RET instruction.  Note
  1105.           that the `10' in this instruction is equal to 4  plus  twice
  1106.           the number  of  parameters of the function.  Also the `8' in
  1107.           the  RET  instruction  is  2  plus  twice  the   number   of
  1108.           parameters.
  1109.  
  1110.           Function CALL
  1111.  
  1112.             Before  we  discuss  function  calling,  we need to assert
  1113.           something about the way expressions will be evaluated.    We
  1114.           will  write  a  general-purpose  procedure  called EVAL that
  1115.           takes an expression root (a SEMREC pointer), then  generates
  1116.           code  such  that  the  arithmetic result of the tree will be
  1117.           left in the AX register.  By asserting this  first,  we  can
  1118.           design  EVAL  to  make  sure  that that is in fact what will
  1119.           happen in every case.  We can also use EVAL itself  to  deal
  1120.  
  1121.  
  1122.                                     page 17
  1123.  
  1124.  
  1125.                            Pascal in 25 Rules or Less
  1126.  
  1127.  
  1128.           with  subtrees,  with  assurance  that  AX  will receive the
  1129.           result.
  1130.             Of course, it's important that EVAL never change the value
  1131.           of BP or set SP capriciously.  It may push some stuff on the
  1132.           stack, but we'll also make sure that whatever is pushed will
  1133.           eventually be popped.  Certain other 8086 registers may also
  1134.           be used in very local situations, for example in  connection
  1135.           with a multiply or divide instruction.
  1136.             Given that EVAL will take an AST tree and produce code for
  1137.           AX, calling  a  function  will  be  very easy.  In fact, the
  1138.           function call will show up in EVAL itself, whenever  we  see
  1139.           the SEMT=funcall!
  1140.             Here's what we need to do (refer to figure 6):
  1141.  
  1142.                PUSH   AX     ; this reserves a return value word
  1143.                eval(P1)     ; parameter P1 is evaluated, result to AX
  1144.                PUSH   AX     ; push result on the stack
  1145.                eval(P2)
  1146.                PUSH   AX
  1147.                  etc.
  1148.                CALL   function
  1149.  
  1150.           We  can  of  course  just  use the function name in the CALL
  1151.           instruction, since the assembler can figure out where it is.
  1152.             Note that this code sequence will work  nicely  within  an
  1153.           expression,  since the result of the CALL is to pop down the
  1154.           stack to exactly the place just before the  first  PUSH  AX,
  1155.           and to yield the function's result in AX.
  1156.             We  can also use this sequence for a procedure call, where
  1157.           the return value is ignored.  AX will  just  get  lost,  and
  1158.           there's no change in the stack.
  1159.  
  1160.           ADD and SUB
  1161.  
  1162.             Let's   now   turn   our  attention  to  evaluating  other
  1163.           expressions.  We need to take  a  close  look  at  the  8086
  1164.           instruction set  and  decide  just how to handle arithmetic.
  1165.           We find that integer addition and  subtraction  are  handled
  1166.           alike,    but    are   coded   somewhat   differently   than
  1167.           multiplication and division.
  1168.             Our ADD or SUB will be between AX and some operand,  which
  1169.           may  be another register value, an immediate value, a memory
  1170.           value or an indirection through a register.   Our  AST  will
  1171.           look like  figure  7.  We imagine that the LEFT subtree will
  1172.           somehow yield AX -- that's easy if we just call EVAL on  the
  1173.           LEFT subtree.    We  would  then  like to emit an ADD or SUB
  1174.           instruction whose operand is the right subtree.
  1175.             Unfortunately, that's not possible if the right subtree is
  1176.           anything other than 1) a literal value, i.e.  SEMT=fixed, or
  1177.           2) a global variable, or 3) a  local  variable.    A  global
  1178.           variable  can  be  accessed by its name alone, while a local
  1179.           will be accessed as an offset through BP, e.g.  6[BP].
  1180.             So before we just go ahead and evaluate the LEFT  subtree,
  1181.           we  need  to test the RIGHT subtree -- is it `simple', i.e.,
  1182.           will it fit into an ADD or SUB instruction  operand  field?
  1183.           If  it  is,  then  we  can  just  EVAL(LEFT),  then emit the
  1184.           appropriate ADD or SUB instruction.
  1185.             The more difficult case in fact requires that we save  the
  1186.  
  1187.  
  1188.                                     page 18
  1189.  
  1190.  
  1191.                            Pascal in 25 Rules or Less
  1192.  
  1193.  
  1194.           result  of  evaluating one of the subtrees before evaluating
  1195.           the other one.  Since we want the left one in AX in order to
  1196.           emit an instruction, we must evaluate the right one  first.
  1197.           That  will  yield the result in AX, but we musn't just leave
  1198.           it there while evaluating the left subtree -- AX  will  very
  1199.           likely get  changed as a result.  Our solution is to PUSH AX
  1200.           before evaluating the LEFT subtree.  After that  evaluation,
  1201.           AX will  hold  the  left subtree.  We choose to POP DX, then
  1202.           emit an ADD or SUB instruction between AX and DX.
  1203.             We therefore arrive at the following compiler  coding  for
  1204.           the ADD and SUB cases of EVAL:
  1205.  
  1206.             addop, subop:
  1207.               if is_simple(right) then begin
  1208.                 eval(left);  {goes to AX}
  1209.                 code3(opcode(semt), ' AX,', nameof(right));
  1210.                 end
  1211.               else begin
  1212.                 eval(right);
  1213.                 code('PUSH AX');  {put in stack temporarily}
  1214.                 eval(left);  {left side to AX}
  1215.                 code('POP DX');  {get the right value back from stack}
  1216.                 code2(opcode(semt), ' AX,DX');
  1217.                 end;
  1218.  
  1219.           Some unfamiliar  functions  are  used in this code fragment.
  1220.           OPCODE(SEMT) returns  a  string  that  represents  the  8086
  1221.           instruction for  the  SEMT.    NAMEOF(root) returns a string
  1222.           that represents a valid operand for the subtree  whose  root
  1223.           is  root  -- it may be a literal constant, a named variable,
  1224.           or an offset-BP form.
  1225.             CODE takes  one  string  and  formats  it  as  a  symbolic
  1226.           assembler line  with  no  label.    A space in the string is
  1227.           interpreted as a tab stop, to  produce  a  pretty  formatted
  1228.           effect in the assembly code.
  1229.             CODE2  takes two strings, concatenates them and calls CODE
  1230.           with the result.  CODE3 and CODE4 are similar.
  1231.             Note that although we pushed a word in the  stack  in  the
  1232.           non-simple case,  it  gets  popped  out two lines later.  We
  1233.           therefore can assert that EVAL will keep the stack  orderly,
  1234.           provided it does so everywhere else.
  1235.  
  1236.           MPY and DIV
  1237.  
  1238.             These  operations are somewhat more messy to encode in the
  1239.           8086.  The IMUL instruction requires one operand in  the  AX
  1240.           register, the  other  in  another register (we will use CX).
  1241.           It returns a result in the pair DX-AX, with AX carrying  the
  1242.           least significant    word.        Without   attempting   any
  1243.           optimizations, we therefore evaluate the right subtree, PUSH
  1244.           it, evaluate the left, then POP CX and emit the opcode.
  1245.             Division requires one additional step -- DX  should  be  a
  1246.           sign extension  of  AX  before dividing by CX.  Fortunately,
  1247.           the instruction CWD  performs  this  for  us.    Here's  the
  1248.           resulting EVAL code fragment:
  1249.  
  1250.             mpyop, divop: begin
  1251.               eval(right);     {divisor to AX}
  1252.  
  1253.  
  1254.                                     page 19
  1255.  
  1256.  
  1257.                            Pascal in 25 Rules or Less
  1258.  
  1259.  
  1260.               code('PUSH AX');
  1261.               eval(left);      {dividend to AX}
  1262.               if semt=divop then code('CWD');  {sign extend into DX}
  1263.               code('POP CX');
  1264.               code2(opcode(semt), ' CX');
  1265.               end;
  1266.  
  1267.           Assignment
  1268.  
  1269.             The  assignment  operation calls for a certain adjustment,
  1270.           depending  on  whether  the  right  subtree  is  a   literal
  1271.           fixed-point number or not.  Here's the EVAL fragment:
  1272.  
  1273.             assignop: begin
  1274.               if right^.semt=fixed then  {an immediate on the right is OK}
  1275.                 code4('MOVW ', nameof(left), ',', nameof(right))
  1276.               else begin
  1277.                 eval(right);  {goes to AX}
  1278.                 code3('MOV ', nameof(left), ',AX');
  1279.                 end
  1280.               end;
  1281.  
  1282.           The use of MOVW is required since the left operand will be a
  1283.           name  or  an  indirect BP reference (we can only assign to a
  1284.           variable or  a  function  return  value),  while  the  right
  1285.           operand could be a byte or a word.  We therefore need a word
  1286.           MOV in this case.
  1287.             In  the  other case, the right subtree is EVALed as usual,
  1288.           and the right MOV operand will be AX,  stamping  this  as  a
  1289.           word operation.    Thus  we  must  nurse the assembler along
  1290.           which otherwise wouldn't have the  foggiest  idea  of  which
  1291.           operation we intend.
  1292.  
  1293.           WHILE DO
  1294.  
  1295.             The  WHILE-DO  form  is  supported by a LEFT pointer and a
  1296.           RIGHT pointer.    LEFT  points  to  an  expression   subtree
  1297.           carrying  the boolean expression (it will be tested for >0 =
  1298.           TRUE, and <=0 = FALSE).  We have  to  encode  this  as  8086
  1299.           branch  instructions,  and have no idea how far the branches
  1300.           must reach.  Thus we use long  branch  instructions,  hoping
  1301.           that  an intelligent assembler can substitute short branches
  1302.           if possible.
  1303.             We need to manufacture a couple of temporary  labels,  and
  1304.           assign that job to a function NEW_LABEL -- this increments a
  1305.           global  label  counter, then appends its string value to the
  1306.           string 'XXX'.  Thus we create new labels such as XXX1, XXX2,
  1307.           etc.  as needed.
  1308.             The function LCODE takes two strings; the first  one  will
  1309.           become an assembler label, and the second the opcode-operand
  1310.           field.  Here's the EVAL code fragment for a WHILE-DO:
  1311.  
  1312.              while_do: begin
  1313.                label1:=new_label;
  1314.                lcode(label1, 'EQU $');
  1315.                eval(left);   {boolean condition}
  1316.                code('CMP AX,0');
  1317.                label2:=new_label;
  1318.  
  1319.  
  1320.                                     page 20
  1321.  
  1322.  
  1323.                            Pascal in 25 Rules or Less
  1324.  
  1325.  
  1326.                code2('JLE ', label2);
  1327.                eval(right);  {statement or statement list}
  1328.                code2('JMP ', label1);
  1329.                lcode(label2, 'EQU $');
  1330.                end;
  1331.  
  1332.           Note  that  we  attach a label before evaluating the boolean
  1333.           condition; this will later appear in a JMP around the  whole
  1334.           WHILE-DO form  back  to  the  boolean  test.   Following the
  1335.           boolean test is a comparison of AX to 0  (recall  that  EVAL
  1336.           yields a  result  in  AX).   We need the CMP since we aren't
  1337.           sure how AX got loaded, hence whether the sign flag  of  the
  1338.           8086 was  set  correctly.    The comparison is followed by a
  1339.           conditional jump to the end of the WHILE-DO form -- note how
  1340.           label2 reappears on an EQU $ as the last coded line.
  1341.             The beauty of EVAL appears between the conditional and the
  1342.           unconditional jump.  It's quite capable of dealing with  any
  1343.           number of statements, function and procedure calls, etc., so
  1344.           that  a  quite large and sophisticated sequence of code will
  1345.           appear in that innocent eval(right) call.  Yet we drop in  a
  1346.           JMP and  the labelled EQU $ at just the right place.  And --
  1347.           the correctness of this evaluation speaks for itself.
  1348.  
  1349.           Statement List
  1350.  
  1351.             We've seen how a sequence of expressions is turned into  a
  1352.           linked list  of  SEMREC  nodes.  We do the same thing with a
  1353.           sequence of statements.  An EVAL fragment can be written for
  1354.           a STMTLIST as follows:
  1355.  
  1356.              stmtlist:
  1357.                while root<>nil do begin
  1358.                  eval(root^.left);
  1359.                  root:=root^.right;
  1360.                  end;
  1361.  
  1362.           The ROOT is of course a value parameter passed to EVAL;  its
  1363.           LEFT  points  to some statement and its RIGHT to the rest of
  1364.           the statement list.  It's OK in Turbo Pascal to  change  the
  1365.           value of a passed value parameter, so we just do it.  Once a
  1366.           ROOT  is evaluated, we don't need it anymore, so we might as
  1367.           well use it to point to the next one.
  1368.             This could also have been coded using a recursive call  on
  1369.           EVAL,  but  sequences  of statements might get very lengthy,
  1370.           and could use up lots of stack space before they unwind.
  1371.  
  1372.           IF-THEN-ELSE
  1373.  
  1374.             Since an IF-THEN-ELSE has three parts, we need  a  special
  1375.           SEMREC clause to deal with it:
  1376.  
  1377.             if_then_else: {if B1 then S1 else S2}
  1378.                (B1, S1, S2: semrecp);
  1379.  
  1380.           This node  of an AST is then easily coded as follows.  We've
  1381.           discussed all the functions that appear in  this  EVAL  code
  1382.           fragment:
  1383.  
  1384.  
  1385.  
  1386.                                     page 21
  1387.  
  1388.  
  1389.                            Pascal in 25 Rules or Less
  1390.  
  1391.  
  1392.              if_then_else: begin
  1393.                label1:=new_label;
  1394.                eval(b1);   {boolean condition}
  1395.                code('CMP AX,0');
  1396.                code2('JLE ', label1);
  1397.                eval(s1);   {THEN statement}
  1398.                label2:=new_label;
  1399.                code2('JMP ', label2);
  1400.                lcode(label1, 'EQU $');
  1401.                eval(s2);   {ELSE statement}
  1402.                lcode(label2, 'EQU $');
  1403.                end;
  1404.  
  1405.  
  1406.           Summary
  1407.  
  1408.             The   following   listing   was  generated  by  the  Chasm
  1409.           assembler.  It is the  compiled  version  of  program  TURUN
  1410.           given near the beginning of this paper.
  1411.             This is  a  complete  program.   It fits into one segment,
  1412.           starting at address 100H.   The  stack  pointer  is  set  to
  1413.           location  STACKORG, which is at the end of a 2000 byte block
  1414.           near the end of the program.  (This is actually  unnecessary
  1415.           since  DOS  will by default set SP to the end of a segment.)
  1416.           The program then begins with a call  to  MAIN.    When  MAIN
  1417.           returns,  an  INT  020H  is  executed, which returns control
  1418.           cleanly to DOS.
  1419.             You can step through  this  program  with  the  DOS  DEBUG
  1420.           system if you want to see what's happening in detail.  Refer
  1421.           to the DOS manual for details.
  1422.             A  few  points  about this assembled listing: A bug in our
  1423.           version of the Chasm assembler (which I've been assured  has
  1424.           been   repaired)   made   it  impossible  to  code  the  RET
  1425.           instruction with a number; instead we've written the  opcode
  1426.           as a  DB followed by the number as a DW.  You'll see this in
  1427.           line 124 for example.
  1428.             Chasm also noticed four JMP  instructions  that  could  be
  1429.           encoded as  short  jumps.    As  we've explained, our simple
  1430.           compiler doesn't keep track of the span between  a  JMP  and
  1431.           its target,  and  that  span  could  be any number of bytes.
  1432.           Thus we've taken the safe approach in our compiler and coded
  1433.           long jumps.  An optimizing assembler could turn  these  into
  1434.           short jumps.
  1435.             Also,  our  compiler  has copied the contents of a support
  1436.           file called STDIO.HDR (not to be confused with the C library
  1437.           of the same name) into the assembly code.    STDIO  supports
  1438.           the WRITE  and  READ functions.  You can see where this file
  1439.           begins and ends from the comments.
  1440.             Are you impressed?  We hope so.  If  you've  followed  our
  1441.           development  and  reasoning  this far, you will have noticed
  1442.           that the code generation  algorithms  for  our  tiny  Pascal
  1443.           statements seem  very  clear  and transparent.  Yet when you
  1444.           look at the generated assembly code, it isn't at  all  clear
  1445.           what's going on.  Some of the PUSH AX instructions came from
  1446.           one  part  of  the  compiler algorithm and some from another
  1447.           part.  But that's the way it is.
  1448.             Many software  writers  prefer  a  high-level  programming
  1449.           language because a few very clear source statements can turn
  1450.  
  1451.  
  1452.                                     page 22
  1453.  
  1454.  
  1455.                            Pascal in 25 Rules or Less
  1456.  
  1457.  
  1458.           into  a  rats  nest  of assembly code -- but if the compiler
  1459.           generates that code correctly, we don't care how  convoluted
  1460.           and hard-to-understand the resulting code is.
  1461.  
  1462.           TURUN.ASM                                                               2/19/86
  1463.           Page: 1                                                                12:56:04
  1464.  
  1465.  
  1466.           LOC  OBJ          LINE  SOURCE                               CHASM version 4.00
  1467.           0100                 1   ; Tiny Pascal assembler code
  1468.           0100 BC290B          2            MOV   SP,OFFSET(STACKORG)
  1469.           0103 8BEC            3            MOV   BP,SP
  1470.           0105 E81901          4            CALL  MAIN
  1471.           0108 CD20            5            INT   020H
  1472.           010A                 6   ; <STDIO.HDR> included
  1473.           010A                 7  ;  STDIO.HDR
  1474.           010A                 8  ;
  1475.           010A                 9  ;  READ and WRITE routines needed for Tiny Pascal
  1476.           010A                10  ;
  1477.           010A                11  SYS_RCHAR   PROC   NEAR   ; Read single character from
  1478.           010A B401           12              MOV    AH,1
  1479.           010C CD21           13              INT    021H
  1480.           010E C3             14              RET           ; value comes back in AL
  1481.           010F                15              ENDP
  1482.           010F                16
  1483.           010F                17  SYS_WRCHAR  PROC   NEAR   ; Write a single character (
  1484.           010F B402           18              MOV    AH,2
  1485.           0111 CD21           19              INT    021H
  1486.           0113 C3             20              RET
  1487.           0114                21              ENDP
  1488.           0114                22
  1489.           0114                23  SYS_WHEX    PROC   NEAR   ; Write a single HEX number
  1490.           0114 80FA0A         24              CMP    DL,10
  1491.           0117 7C07           25              JL     SYS_01
  1492.           0119 80C237         26              ADD    DL,55     ; 'A' - 10
  1493.           011C E8F0FF         27              CALL   SYS_WRCHAR
  1494.           011F C3             28              RET
  1495.           0120 80C230         29  SYS_01      ADD    DL,'0'
  1496.           0123 E8E9FF         30              CALL   SYS_WRCHAR
  1497.           0126 C3             31              RET
  1498.           0127                32              ENDP
  1499.           0127                33
  1500.           0127                34  SYS_IWRT    PROC   NEAR   ; Write an integer to stdout
  1501.           0127 B604           35              MOV    DH,4   ; used as a counter
  1502.           0129 D1C0           36  SYS_11      ROL    AX
  1503.           012B D1C0           37              ROL    AX
  1504.           012D D1C0           38              ROL    AX
  1505.           012F D1C0           39              ROL    AX
  1506.           0131 8AD0           40              MOV    DL,AL
  1507.           0133 80E20F         41              AND    DL,0FH
  1508.           0136 50             42              PUSH   AX
  1509.           0137 E8DAFF         43              CALL   SYS_WHEX
  1510.           013A 58             44              POP    AX
  1511.           013B FECE           45              DEC    DH
  1512.           013D 75EA           46              JNZ    SYS_11
  1513.           013F C3             47              RET
  1514.           0140                48              ENDP
  1515.           0140                49
  1516.  
  1517.  
  1518.                                     page 23
  1519.  
  1520.  
  1521.                            Pascal in 25 Rules or Less
  1522.  
  1523.  
  1524.           0140                50  SYS_SWRT    PROC   NEAR   ; Write a string terminated
  1525.           0140 8A5700         51  SYS_21      MOV    DL,0[BX]
  1526.           0143 80FA00         52              CMP    DL,0
  1527.           0146 7501           53              JNZ    SYS_22   ; zero terminator?
  1528.           0148 C3             54              RET
  1529.           0149 E8C3FF         55  SYS_22      CALL   SYS_WRCHAR
  1530.           014C 43             56              INC    BX
  1531.           014D EBF1           57              JMPS   SYS_21
  1532.           014F                58              ENDP
  1533.           014F                59
  1534.           014F                60  SYS_WRTLN   PROC   NEAR    ; write carriage return/lin
  1535.           014F B20D           61              MOV    DL,0DH
  1536.           0151 E8BBFF         62              CALL   SYS_WRCHAR
  1537.           0154 B20A           63              MOV    DL,0AH
  1538.           0156 E8B6FF         64              CALL   SYS_WRCHAR
  1539.           0159 C3             65              RET
  1540.           015A                66              ENDP
  1541.           015A                67
  1542.           015A                68  READ        PROC   NEAR         ; read a HEX number fr
  1543.           015A BA0000         69              MOV    DX,0         ; clear DX
  1544.           015D E8AAFF         70  READ_01     CALL   SYS_RCHAR    ; get one character in
  1545.           0160                71                      ; won't affect DX
  1546.           0160 3C0D           72              CMP    AL,0DH
  1547.           0162 7506           73              JNZ    READ_02
  1548.           0164 52             74              PUSH   DX           ; save the thing we've
  1549.           0165 E8E7FF         75              CALL   SYS_WRTLN    ; send a carriage retu
  1550.           0168 58             76              POP    AX           ; was an ENTER
  1551.           0169 C3             77              RET
  1552.           016A 3C20           78  READ_02     CMP    AL,' '
  1553.           016C 74EF           79              JZ     READ_01      ; ignore spaces
  1554.           016E 2C30           80              SUB    AL,'0'       ; start conversion to
  1555.           0170 3C09           81              CMP    AL,9
  1556.           0172 7E02           82              JLE    READ_03
  1557.           0174 2C07           83              SUB    AL,7         ; turn 'A' into 0AH
  1558.           0176 3C0F           84  READ_03     CMP    AL,0FH
  1559.           0178 7E02           85              JLE    READ_04
  1560.           017A 2C20           86              SUB    AL,32        ; turn 'a' into 0AH
  1561.           017C 240F           87  READ_04     AND    AL,0FH       ; clip for good measur
  1562.           017E D1E2           88              SHL    DX           ; prepare DX for hex v
  1563.           0180 D1E2           89              SHL    DX
  1564.           0182 D1E2           90              SHL    DX
  1565.           0184 D1E2           91              SHL    DX
  1566.           0186 08C2           92              OR     DL,AL
  1567.           0188 EBD3           93              JMPS   READ_01      ; go do some more
  1568.           018A                94              ENDP
  1569.           018A                95
  1570.           018A                96  READLN      PROC   NEAR
  1571.           018A EBCE           97              JMPS   READ         ; does the same thing
  1572.           018C                98              ENDP
  1573.           018C                99
  1574.           018C               100   ; ... end of include STDIO.HDR
  1575.           018C               101   ;   {TURUN -- A sample program written in Tiny Pascal
  1576.           018C               102   ;   var I, J, K, PROBLEM;
  1577.           018C               103   ;
  1578.           018C               104   ;   {*********************}
  1579.           018C               105   ;   function ISLESS(N1, N2);
  1580.           018C               106   ;   begin  {returns 1 if n1<n2, 0 otherwise}
  1581.           018C               107   ;     if n2-n1 then isless:=1   {truth value test is
  1582.  
  1583.  
  1584.                                     page 24
  1585.  
  1586.  
  1587.                            Pascal in 25 Rules or Less
  1588.  
  1589.  
  1590.           018C               108   ;     else isless:=0;
  1591.           018C               109   ;     end;
  1592.           018C               110  ISLESS    PROC  NEAR
  1593.           018C 55            111            PUSH  BP
  1594.           018D 8BEC          112            MOV   BP,SP
  1595.           018F 8B4604        113            MOV   AX,4[BP] ; N2
  1596.           0192 2B4606        114            SUB   AX,6[BP] ; N1
  1597.           0195 3D0000        115            CMP   AX,0
  1598.           0198 7E08          116            JLE   XXX0
  1599.           019A C746080100    117            MOVW  8[BP],1 ; ISLESS
  1600.           **** Diagnostic: Could use JMPS
  1601.           019F E90500        118            JMP   XXX1
  1602.           01A2               119  XXX0      EQU   $
  1603.           01A2 C746080000    120            MOVW  8[BP],0 ; ISLESS
  1604.           01A7               121  XXX1      EQU   $
  1605.           01A7 8B4608        122            MOV   AX,8[BP] ; ISLESS
  1606.           01AA 5D            123            POP   BP
  1607.           01AB C2            124            DB    0C2H
  1608.           01AC 0600          125            DW    6
  1609.           01AE               126            ENDP
  1610.           01AE               127   ;    SYMBOL TABLE
  1611.           01AE               128   ; ISLESS                          8[BP]
  1612.           01AE               129   ; N1                              6[BP]
  1613.           01AE               130   ; N2                              4[BP]
  1614.           01AE               131
  1615.           01AE               132   ;
  1616.           01AE               133   ;   function ADDEMUP(LOWER, UPPER, SUM);
  1617.           01AE               134   ;   begin end;    {makes it a forward declaration}
  1618.           01AE               135   ;
  1619.           01AE               136   ;   {*********************}
  1620.           01AE               137   ;   function ISEQUAL(N1, N2);
  1621.           01AE               138   ;   begin
  1622.           01AE               139   ;     if n2-n1 then isequal:=0   {false}
  1623.           01AE               140   ;     else
  1624.           01AE               141   ;     if n1-n2 then isequal:=0
  1625.           01AE               142   ;     else isequal:=1;
  1626.           01AE               143   ;     end;
  1627.           01AE               144  ISEQUAL   PROC  NEAR
  1628.           01AE 55            145            PUSH  BP
  1629.           01AF 8BEC          146            MOV   BP,SP
  1630.           01B1 8B4604        147            MOV   AX,4[BP] ; N2
  1631.           01B4 2B4606        148            SUB   AX,6[BP] ; N1
  1632.           01B7 3D0000        149            CMP   AX,0
  1633.           01BA 7E08          150            JLE   XXX2
  1634.           01BC C746080000    151            MOVW  8[BP],0 ; ISEQUAL
  1635.           **** Diagnostic: Could use JMPS
  1636.           01C1 E91800        152            JMP   XXX3
  1637.           01C4               153  XXX2      EQU   $
  1638.           01C4 8B4606        154            MOV   AX,6[BP] ; N1
  1639.           01C7 2B4604        155            SUB   AX,4[BP] ; N2
  1640.           01CA 3D0000        156            CMP   AX,0
  1641.           01CD 7E08          157            JLE   XXX4
  1642.           01CF C746080000    158            MOVW  8[BP],0 ; ISEQUAL
  1643.           **** Diagnostic: Could use JMPS
  1644.           01D4 E90500        159            JMP   XXX5
  1645.           01D7               160  XXX4      EQU   $
  1646.           01D7 C746080100    161            MOVW  8[BP],1 ; ISEQUAL
  1647.           01DC               162  XXX5      EQU   $
  1648.  
  1649.  
  1650.                                     page 25
  1651.  
  1652.  
  1653.                            Pascal in 25 Rules or Less
  1654.  
  1655.  
  1656.           01DC               163  XXX3      EQU   $
  1657.           01DC 8B4608        164            MOV   AX,8[BP] ; ISEQUAL
  1658.           01DF 5D            165            POP   BP
  1659.           01E0 C2            166            DB    0C2H
  1660.           01E1 0600          167            DW    6
  1661.           01E3               168            ENDP
  1662.           01E3               169   ;    SYMBOL TABLE
  1663.           01E3               170   ; ISEQUAL                         8[BP]
  1664.           01E3               171   ; N1                              6[BP]
  1665.           01E3               172   ; N2                              4[BP]
  1666.           01E3               173
  1667.           01E3               174   ;
  1668.           01E3               175   ;   {***********************}
  1669.           01E3               176   ;   function ADDEMUP(LOWER, UPPER, SUM);
  1670.           01E3               177   ;         {SUM is a local}
  1671.           01E3               178   ;   begin
  1672.           01E3               179   ;     sum:=0;
  1673.           01E3               180   ;     while isless(lower, upper) do begin
  1674.           01E3               181   ;       sum:=sum+lower;
  1675.           01E3               182   ;       lower:=lower+1;
  1676.           01E3               183   ;       end;
  1677.           01E3               184   ;     addemup:=sum+lower;  { the last one was left ou
  1678.           01E3               185   ;     end;
  1679.           01E3               186  ADDEMUP   PROC  NEAR
  1680.           01E3 55            187            PUSH  BP
  1681.           01E4 8BEC          188            MOV   BP,SP
  1682.           01E6 C746040000    189            MOVW  4[BP],0 ; SUM
  1683.           01EB               190  XXX6      EQU   $
  1684.           01EB 50            191            PUSH  AX
  1685.           01EC 8B4608        192            MOV   AX,8[BP] ; LOWER
  1686.           01EF 50            193            PUSH  AX
  1687.           01F0 8B4606        194            MOV   AX,6[BP] ; UPPER
  1688.           01F3 50            195            PUSH  AX
  1689.           01F4 E895FF        196            CALL  ISLESS
  1690.           01F7 3D0000        197            CMP   AX,0
  1691.           01FA 7E15          198            JLE   XXX7
  1692.           01FC 8B4604        199            MOV   AX,4[BP] ; SUM
  1693.           01FF 034608        200            ADD   AX,8[BP] ; LOWER
  1694.           0202 894604        201            MOV   4[BP],AX ; SUM
  1695.           0205 8B4608        202            MOV   AX,8[BP] ; LOWER
  1696.           0208 050100        203            ADD   AX,1
  1697.           020B 894608        204            MOV   8[BP],AX ; LOWER
  1698.           **** Diagnostic: Could use JMPS
  1699.           020E E9DAFF        205            JMP   XXX6
  1700.           0211               206  XXX7      EQU   $
  1701.           0211 8B4604        207            MOV   AX,4[BP] ; SUM
  1702.           0214 034608        208            ADD   AX,8[BP] ; LOWER
  1703.           0217 89460A        209            MOV   10[BP],AX ; ADDEMUP
  1704.           021A 8B460A        210            MOV   AX,10[BP] ; ADDEMUP
  1705.           021D 5D            211            POP   BP
  1706.           021E C2            212            DB    0C2H
  1707.           021F 0800          213            DW    8
  1708.           0221               214            ENDP
  1709.           0221               215   ;    SYMBOL TABLE
  1710.           0221               216   ; ADDEMUP                         10[BP]
  1711.           0221               217   ; LOWER                           8[BP]
  1712.           0221               218   ; UPPER                           6[BP]
  1713.           0221               219   ; SUM                             4[BP]
  1714.  
  1715.  
  1716.                                     page 26
  1717.  
  1718.  
  1719.                            Pascal in 25 Rules or Less
  1720.  
  1721.  
  1722.           0221               220
  1723.           0221               221   ;
  1724.           0221               222   ;   {*********************}
  1725.           0221               223   ;   function MAIN(SUM, UPPER);
  1726.           0221               224   ;   begin
  1727.           0221               225   ;     i:=1;
  1728.           0221               226   ;     j:=i+5;
  1729.           0221               227   ;     k:=j-16;
  1730.           0221               228   ;     problem:=i+(j*k);
  1731.           0221               229   ;     writeln('I: ', i, ' J: ', j, ' K: ', k, ' Probl
  1732.           0221               230   ;     write('Enter upper ');
  1733.           0221               231   ;     upper:=read;
  1734.           0221               232   ;     sum:=addemup(1, upper);  {sum of integers 1..up
  1735.           0221               233   ;     if isequal(sum, (upper*(upper+1))/2) then
  1736.           0221               234   ;       writeln('Sum = ', sum)
  1737.           0221               235   ;     else begin
  1738.           0221               236   ;       writeln('BUG: Sum = ', sum, '; should be ',
  1739.           0221               237   ;                  (upper*(upper+1))/2);
  1740.           0221               238   ;       end;
  1741.           0221               239   ;     end;
  1742.           0221               240  MAIN      PROC  NEAR
  1743.           0221 55            241            PUSH  BP
  1744.           0222 8BEC          242            MOV   BP,SP
  1745.           0224 C70653030100  243            MOVW  I,1 ; I
  1746.           022A A15303        244            MOV   AX,I ; I
  1747.           022D 050500        245            ADD   AX,5
  1748.           0230 A35503        246            MOV   J,AX ; J
  1749.           0233 A15503        247            MOV   AX,J ; J
  1750.           0236 2D1000        248            SUB   AX,16
  1751.           0239 A35703        249            MOV   K,AX ; K
  1752.           023C A15703        250            MOV   AX,K ; K
  1753.           023F 50            251            PUSH  AX
  1754.           0240 A15503        252            MOV   AX,J ; J
  1755.           0243 59            253            POP   CX
  1756.           0244 F7E9          254            IMULW CX
  1757.           0246 50            255            PUSH  AX
  1758.           0247 A15303        256            MOV   AX,I ; I
  1759.           024A 5A            257            POP   DX
  1760.           024B 01D0          258            ADD   AX,DX
  1761.           024D A35103        259            MOV   PROBLEM,AX ; PROBLEM
  1762.           0250 BB4D03        260            MOV   BX,OFFSET(SS0)
  1763.           0253 E8EAFE        261            CALL  SYS_SWRT
  1764.           0256 A15303        262            MOV   AX,I ; I
  1765.           0259 E8CBFE        263            CALL  SYS_IWRT
  1766.           025C BB4803        264            MOV   BX,OFFSET(SS1)
  1767.           025F E8DEFE        265            CALL  SYS_SWRT
  1768.           0262 A15503        266            MOV   AX,J ; J
  1769.           0265 E8BFFE        267            CALL  SYS_IWRT
  1770.           0268 BB4303        268            MOV   BX,OFFSET(SS2)
  1771.           026B E8D2FE        269            CALL  SYS_SWRT
  1772.           026E A15703        270            MOV   AX,K ; K
  1773.           0271 E8B3FE        271            CALL  SYS_IWRT
  1774.           0274 BB3803        272            MOV   BX,OFFSET(SS3)
  1775.           0277 E8C6FE        273            CALL  SYS_SWRT
  1776.           027A A15103        274            MOV   AX,PROBLEM ; PROBLEM
  1777.           027D E8A7FE        275            CALL  SYS_IWRT
  1778.           0280 E8CCFE        276            CALL  SYS_WRTLN
  1779.           0283 BB2B03        277            MOV   BX,OFFSET(SS4)
  1780.  
  1781.  
  1782.                                     page 27
  1783.  
  1784.  
  1785.                            Pascal in 25 Rules or Less
  1786.  
  1787.  
  1788.           0286 E8B7FE        278            CALL  SYS_SWRT
  1789.           0289 E8CEFE        279            CALL  READ
  1790.           028C 894604        280            MOV   4[BP],AX ; UPPER
  1791.           028F 50            281            PUSH  AX
  1792.           0290 B80100        282            MOV   AX,1
  1793.           0293 50            283            PUSH  AX
  1794.           0294 8B4604        284            MOV   AX,4[BP] ; UPPER
  1795.           0297 50            285            PUSH  AX
  1796.           0298 B80000        286            MOV   AX,0
  1797.           029B 50            287            PUSH  AX
  1798.           029C E844FF        288            CALL  ADDEMUP
  1799.           029F 894606        289            MOV   6[BP],AX ; SUM
  1800.           02A2 50            290            PUSH  AX
  1801.           02A3 8B4606        291            MOV   AX,6[BP] ; SUM
  1802.           02A6 50            292            PUSH  AX
  1803.           02A7 B80200        293            MOV   AX,2
  1804.           02AA 50            294            PUSH  AX
  1805.           02AB 8B4604        295            MOV   AX,4[BP] ; UPPER
  1806.           02AE 050100        296            ADD   AX,1
  1807.           02B1 50            297            PUSH  AX
  1808.           02B2 8B4604        298            MOV   AX,4[BP] ; UPPER
  1809.           02B5 59            299            POP   CX
  1810.           02B6 F7E9          300            IMULW CX
  1811.           02B8 99            301            CWD
  1812.           02B9 59            302            POP   CX
  1813.           02BA F7F9          303            IDIVW CX
  1814.           02BC 50            304            PUSH  AX
  1815.           02BD E8EEFE        305            CALL  ISEQUAL
  1816.           02C0 3D0000        306            CMP   AX,0
  1817.           02C3 7E12          307            JLE   XXX8
  1818.           02C5 BB2403        308            MOV   BX,OFFSET(SS5)
  1819.           02C8 E875FE        309            CALL  SYS_SWRT
  1820.           02CB 8B4606        310            MOV   AX,6[BP] ; SUM
  1821.           02CE E856FE        311            CALL  SYS_IWRT
  1822.           02D1 E87BFE        312            CALL  SYS_WRTLN
  1823.           **** Diagnostic: Could use JMPS
  1824.           02D4 E92D00        313            JMP   XXX9
  1825.           02D7               314  XXX8      EQU   $
  1826.           02D7 BB1803        315            MOV   BX,OFFSET(SS6)
  1827.           02DA E863FE        316            CALL  SYS_SWRT
  1828.           02DD 8B4606        317            MOV   AX,6[BP] ; SUM
  1829.           02E0 E844FE        318            CALL  SYS_IWRT
  1830.           02E3 BB0B03        319            MOV   BX,OFFSET(SS7)
  1831.           02E6 E857FE        320            CALL  SYS_SWRT
  1832.           02E9 B80200        321            MOV   AX,2
  1833.           02EC 50            322            PUSH  AX
  1834.           02ED 8B4604        323            MOV   AX,4[BP] ; UPPER
  1835.           02F0 050100        324            ADD   AX,1
  1836.           02F3 50            325            PUSH  AX
  1837.           02F4 8B4604        326            MOV   AX,4[BP] ; UPPER
  1838.           02F7 59            327            POP   CX
  1839.           02F8 F7E9          328            IMULW CX
  1840.           02FA 99            329            CWD
  1841.           02FB 59            330            POP   CX
  1842.           02FC F7F9          331            IDIVW CX
  1843.           02FE E826FE        332            CALL  SYS_IWRT
  1844.           0301 E84BFE        333            CALL  SYS_WRTLN
  1845.           0304               334  XXX9      EQU   $
  1846.  
  1847.  
  1848.                                     page 28
  1849.  
  1850.  
  1851.                            Pascal in 25 Rules or Less
  1852.  
  1853.  
  1854.           0304 8B4608        335            MOV   AX,8[BP] ; MAIN
  1855.           0307 5D            336            POP   BP
  1856.           0308 C2            337            DB    0C2H
  1857.           0309 0600          338            DW    6
  1858.           030B 3B2073686F75  339  SS7       DB    '; should be ',0
  1859.                6C6420626520
  1860.                00
  1861.           0318 4255473A2053  340  SS6       DB    'BUG: Sum = ',0
  1862.                756D203D2000
  1863.           0324 53756D203D20  341  SS5       DB    'Sum = ',0
  1864.                00
  1865.           032B 456E74657220  342  SS4       DB    'Enter upper ',0
  1866.                757070657220
  1867.                00
  1868.           0338 2050726F626C  343  SS3       DB    ' Problem: ',0
  1869.                656D3A2000
  1870.           0343 204B3A2000    344  SS2       DB    ' K: ',0
  1871.           0348 204A3A2000    345  SS1       DB    ' J: ',0
  1872.           034D 493A2000      346  SS0       DB    'I: ',0
  1873.           0351               347            ENDP
  1874.           0351               348   ;    SYMBOL TABLE
  1875.           0351               349   ; MAIN                            8[BP]
  1876.           0351               350   ; SUM                             6[BP]
  1877.           0351               351   ; UPPER                           4[BP]
  1878.           0351               352
  1879.           0351               353   ;
  1880.           0351               354   ; GLOBAL VARIABLES
  1881.           0351 0000          355  PROBLEM   DW    0
  1882.           0353 0000          356  I         DW    0
  1883.           0355 0000          357  J         DW    0
  1884.           0357 0000          358  K         DW    0
  1885.           0359               359   ; RUNTIME STACK
  1886.           0359               360            DS    2000
  1887.           0B29 0000          361  STACKORG  DW    0
  1888.           0B2B               362   ; MAIN stack space
  1889.           0B2B 0000          363            DW    0
  1890.           0B2D 0000          364            DW    0
  1891.           0B2F 0000          365            DW    0
  1892.           0B31               366  ; NO errors
  1893.  
  1894.  
  1895.              0 Error(s) detected
  1896.              5 Diagnostic(s) offered
  1897.  
  1898.  
  1899.           817 (331H) Bytes of object code generated
  1900.  
  1901.           Symbol Table Dump:
  1902.  
  1903.           ADDEMUP.........P01E3     I...............M0353     ISEQUAL.........P01AE
  1904.           ISLESS..........P018C     J...............M0355     K...............M0357
  1905.           MAIN............P0221     PROBLEM.........M0351     READ............P015A
  1906.           READLN..........P018A     READ_01.........P015D     READ_02.........P016A
  1907.           READ_03.........P0176     READ_04.........P017C     SS0.............M034D
  1908.           SS1.............M0348     SS2.............M0343     SS3.............M0338
  1909.           SS4.............M032B     SS5.............M0324     SS6.............M0318
  1910.           SS7.............M030B     STACKORG........M0B29     SYS_01..........P0120
  1911.           SYS_11..........P0129     SYS_21..........P0140     SYS_22..........P0149
  1912.  
  1913.  
  1914.                                     page 29
  1915.  
  1916.  
  1917.                            Pascal in 25 Rules or Less
  1918.  
  1919.  
  1920.           SYS_IWRT........P0127     SYS_RCHAR.......P010A     SYS_SWRT........P0140
  1921.           SYS_WHEX........P0114     SYS_WRCHAR......P010F     SYS_WRTLN.......P014F
  1922.           XXX0............P01A2     XXX1............P01A7     XXX2............P01C4
  1923.           XXX3............P01DC     XXX4............P01D7     XXX5............P01DC
  1924.           XXX6............P01EB     XXX7............P0211     XXX8............P02D7
  1925.           XXX9............P0304
  1926.  
  1927.           Where to Obtain Software
  1928.  
  1929.             We  invite  you  to  try  writing your own compiler, or at
  1930.           least to try the tools and source code we  used  in  writing
  1931.           this one.  Here's what you need -- you'll find the cost very
  1932.           reasonable:
  1933.  
  1934.             The Qparser  demo  system.   Order from QCAD Systems, 1164
  1935.           Hyde  Ave,  San   Jose,   CA   95129,   phone   800/538-9787
  1936.           (408/727-6671 in  CA).    Price  is $10 plus $2 shipping and
  1937.           handling.  CA residents add sales tax.  Check, money  order,
  1938.           or bank  card  accepted.    This  comes  with a booklet that
  1939.           carries you through a simple pocket calculator example,  but
  1940.           contains  the  all-important parser generator needed for our
  1941.           tiny Pascal  compiler.    If  you  want  to  go  beyond   25
  1942.           productions,  the  unlimited  Qparser  system  will cost you
  1943.           $400.
  1944.  
  1945.             Source for the tiny Pascal  compiler.    Order  from  QCAD
  1946.           Systems, same  address  and  phones  as  above.  Ask for the
  1947.           "tiny Pascal disc": $10 plus $2 shipping and handling,  plus
  1948.           sales tax, etc.
  1949.  
  1950.             The Chasm assembler.  You can purchase an abridged version
  1951.           of  this  assembler  (but  powerful  enough  to support tiny
  1952.           Pascal) from PC Software Interest Group,  1030  E.    Duane,
  1953.           suite D,   Sunnyvale,  CA  94086.    Call  408/730-9291  for
  1954.           membership and ordering information.  Ask  for  disk  number
  1955.           10.   A  membership  in  PC-SIG  is  $20  for  one year, and
  1956.           entitles you to any number of software discs at $6 each from
  1957.           their catalog of over 540 discs.
  1958.             You can also order a full Chasm assembler for $40 directly
  1959.           from the author,  David  Whitman,  136  Wellington  Terrace,
  1960.           Lansdale, PA, 19446, phones 215/641-7114 (day), 215/362-8526
  1961.           (evenings).
  1962.             Tiny  Pascal  was written to be compatible with Chasm, but
  1963.           it can easily be adapted to other assembler conventions.
  1964.             The Qparser demo system disc  can  also  be  ordered  from
  1965.           PC-SIG for $6 -- ask for disk number 419.  (This version has
  1966.           the booklet information on the disc as a README file.)
  1967.  
  1968.           References
  1969.  
  1970.             [1]  Compiler  Construction--Theory and Practice, Barrett,
  1971.           Bates, Gustafson and  Couch,  Science  Research  Associates,
  1972.           Chicago, second  edition.    This is a companion textbook to
  1973.           the Qparser system.  This  and  reference  [2]  develop  the
  1974.           theory  of  grammars,  parsers,  translation,  symbol  table
  1975.           management and much more.
  1976.  
  1977.             [2]  Principles  of  Compiler  Design,  Aho  and   Ullman,
  1978.  
  1979.  
  1980.                                     page 30
  1981.  
  1982.  
  1983.                            Pascal in 25 Rules or Less
  1984.  
  1985.  
  1986.           Addison-Wesley.
  1987.  
  1988.             [3] 8086  Family  User's  Manual,  Intel Corp, 9800722-03.
  1989.           Order from the Literature Dept, Intel Corp, 3065 Bowers Ave,
  1990.           Santa Clara, CA, 95051.  A definitive  volume  on  the  8086
  1991.           architecture,  development  systems,  software  and  related
  1992.           semiconductor products.
  1993.  
  1994.  
  1995.  
  1996.  
  1997.  
  1998.  
  1999.  
  2000.  
  2001.  
  2002.  
  2003.  
  2004.  
  2005.  
  2006.  
  2007.  
  2008.  
  2009.  
  2010.  
  2011.  
  2012.  
  2013.  
  2014.  
  2015.  
  2016.  
  2017.  
  2018.  
  2019.  
  2020.  
  2021.  
  2022.  
  2023.  
  2024.  
  2025.  
  2026.  
  2027.  
  2028.  
  2029.  
  2030.  
  2031.  
  2032.  
  2033.  
  2034.  
  2035.  
  2036.  
  2037.  
  2038.  
  2039.  
  2040.  
  2041.  
  2042.  
  2043.  
  2044.  
  2045.  
  2046.                                     page 31
  2047.